#define _CRT_SECURE_NO_WARNINGS 1



#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int* status);
pid_t waitpid(pid_t pid，int* status, int options);


————————————————————————————————————————————————————————————————


		< 阻塞等待 >
pid_t waitpid(pid_t pid，int* status,  0 );

			< WIFEXITED / WEXITSTATUS >
一般不使用 status 直接使用 宏：
WIFEXITED(status)   :若为正常终止子进程返回的状态，则为真。(查看进程是否是正常退出)
WEXITSTATUS(status) : 若wIFEXITED非零，提取子进程退出码。(查看进程的退出码)
options :
	WNOHANG : 若pid指定的子进程没有结束，则waitpid()函数返回e，不予以等待。
	若正常结束，则返回该子进程的ID。

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/wait.h>
  7 
  8 void childRun()
  9 {
 10         //int *p = NULL;  //测试异常时用
 11         int cnt = 5;
 12         while (cnt)
 13         {
 14                 printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(),     getppid(), cnt);
 15                 sleep(1);
 16                 cnt--;
 17                 //*p = 100;
 18         }
 19 }
 20 
 21 int main()
 22 {
 23         printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
 24         pid_t id = fork();
 25         if (id == 0)
 26         {
 27                 // child
 28                 childRun();
 29                 printf("child quit ...\n");
 30                 exit(123);
 31         }
 32         sleep(10);
 33         // fahter
 34         //pid_t rid = wait(NULL);
 35         int status = 0;
 36         pid_t rid = waitpid(id, &status, 0);
 37         if (rid > 0)
 38         {
 39                 if(WIFEXITED(status))
 40                 {
 41                         
 42                         //printf("father quit ... status: %d, child quit code : %d,     child quit signal: %d\n", status, (status>>8)&0xff, status&0x7f);   
 43                         printf("child quit successs, exit code: %d\n", WEXITSTATUS(    status));
 44                 }
 45                 else
 46                 {
 47                         printf("child quit unnormal!\n");
 48                 }
 49                 printf("wait success,rid: %d\n", rid);
 50         }
 51         else
 52         {
 53                 printf("wait failed!\n");
 54         }
 55         sleep(3);
 56         printf("father quit ... status: %d\n", status);
 57         //printf("father quit ... status: %d, child quit code : %d, child quit sign    al: %d\n", status, (status>>8)&0xff, status&0x7f);   
 58         
 59         return 0;
 60 }


[a@192 h]$ . / myprocess
I am father, pid: 3539, ppid : 2855
I am child process, pid : 3540, ppid : 3539, cnt : 5
//  ... 
I am child process, pid : 3540, ppid : 3539, cnt : 1
child quit ...
child quit successs, exit code : 123
wait success, rid : 3540
father quit ... status : 31488
[a@192 h]$


如果子进程没有退出
而父进程在进行执行waitpid进行等待，阻塞等待, 如同scanf 状态
在等待某种条件发生（子程序退出）

————————————————————————————————————————————————————————————————


		< 宏：WNOHANG (非阻塞等待) >

pid_t waitpid(pid_t pid，int* status, int options);
pid_t waitpid(pid_t pid，int* status,   WNOHANG  );


[阻塞等待] :						//期间父进程什么也不做，一直等待。
pid_t >  0 : 等待成功的，子进程退出了，并且父进程回收成功
pid_t <  0 : 等待失败了。

[非阻塞等待]: 
pid_t == 0 : 检测是成功的，只不过子进程还没退出，需要你下一次进行重复等待
非阻塞等待的时候一般由：
非阻塞等待＋循环 = 非阻塞轮询		//期间允许父进程做一些其他的事情。


————————————————————————————————————————————————————————————————


			< 非阻塞轮询 >

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

void childRun()
{
    //int *p = NULL;
    int cnt = 5;
    while (cnt)
    {
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
        sleep(1);
        cnt--;
        //*p = 100;
    }
}

int main()
{
    printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
    pid_t id = fork();
    if (id == 0)
    {
        // child
        childRun();
        printf("child quit ...\n");
        exit(123);
    }
    //sleep(10);

    // fahter
    while (1)
    {
        int status = 0;
        pid_t rid = waitpid(id, &status, WNOHANG);      // non block
        if (rid == 0)
        {
            usleep(1000);
            printf("child is running, father check next time!\n");
            //DoOtherThing();

        }
        else if (rid > 0)
        {
            if (WIFEXITED(status))
            {
                printf("child quit successs, exit code: %d\n", WEXITSTATUS(status));
            }
            else
            {
                printf("child quit unnormal!\n");
            }
            break;
        }
        else
        {
            printf("wait failed!\n");
            break;
        }
    }
    return 0;
}

运行结果：
[a@192 h]$ while :; do ps axj | head - 1 && ps axj | grep myprocess | grep - v grep; sleep 1; done
[a@192 h]$ . / myprocess
child is running, father check next time!
  ... ...
child is running, father check next time!
child is running, father check next time!
child quit ...
child is running, father check next time!
child quit successs, exit code : 123


————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————


		《 进程的程序替换 》

站在被替换进程的角度:本质就是这个程序被加载到内存了!
 exec* 类似于—种Linux上的加载函数

#include <unistd.h>
extern char **environ; (3号手册...)
int exec1(const char* path, Iconst char* arg, ...);		// '...'可变参数
int execlp(const char* file, const char* arg, ...); 
int execle(const char* path，const char* arg,..., char* const envp[]);
int execv(const char* path, char* const argv[]);
int execvp(const char* file, char* const argv[]); 
int execvpe(const char* file，char* const argv[],char* const envp[]);

 EXECVE(2号手册)，系统调用
int execve(const char* filename，char* const argv[],char* const envp[]);

————————————————————————————————————————————————————————————————

[a@192 i] $ cat testexec.c
#include <stdio.h>
#include <unistd.h>

int main()
{
	printf("testexec ... begin!\n");

	execl("/usr/bin/ls", "ls", "-l", "-a", NULL);	// 把它后面的代码替换为，新的代码，原进程 PCB 不变，只修改对应的页表值。执行新覆盖的代码。

	printf("testexec ... end!\n");					// 由于后续代码被替换了，已经不在该进程中了，所以后续代码无法继续执行了。

	return 0;
}
 
		< 替换成功 >
[a@192 i]$ . / testexec
testexec ... begin!
total 20
drwxrwxr - x.  2 a a   56 Apr  6 22:35 .
drwxrwxr - x. 14 a a  198 Apr  6 22 : 28 ..
- rw - rw - r--.  1 a a  109 Apr  6 22 : 31 Makefile
- rwxrwxr - x.  1 a a 8448 Apr  6 22 : 35 testexec
- rw - rw - r--.  1 a a  186 Apr  6 22 : 34 testexec.c

execl函数的返回值可以不关心了。
只要替换成功，就不会向后继续运行，
只要继续运行了，一定是替换失败了!

		< 替换失败 >
testexec ... begin!
替换失败:继续执行原来的后续代码。
testexec ... end!


————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————


		< 改成多进程版:程序替换 >

创建子进程，让子进程完成任务：
1.让子进程执行父进程代码的一部分。
2.让子进程执行一个全新的程序。
父子进程，具有独立性。
当子进程，需要修改磁盘中拷贝过来的代码时，代码也发生【写时拷贝】，此时父子代码层面也完全独立开来。	 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("testexec ... begin!\n");

    pid_t id = fork();
    if (id == 0)
    {
        //child
        sleep(2);
        //execl("/usr/bin/lss", "lss", "-l", "-a", NULL); //一个不存在的路径,子进程执行失败，
        execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
        //execl("/usr/bin/top", "top", NULL);				//子进程运行了任务管理。任何命令都能替换。

        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");

    return 0;
}


子进程执行失败：
[a@192 i]$ ls
Makefile  testexec  testexec.c
[a@192 i]$ . / testexec
testexec ... begin!
father wait success, child exit code : 1
testexec ... end!
[a@192 i]$

 ...
 [a@192 i]$ while :; do ps axj | head -1 && ps axj | gewp testexec | grep -v grep; sleep 1; done
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
bash : gewp: command not found...
 ...

子进程执行成功：
[a@192 i]$ . / testexec
testexec ... begin!
total 20
drwxrwxr - x.  2 a a   56 Apr 11 09:54 .
drwxrwxr - x. 14 a a  198 Apr  6 22 : 28 ..
- rw - rw - r--.  1 a a  109 Apr  6 22 : 31 Makefile
- rwxrwxr - x.  1 a a 8704 Apr 11 09 : 54 testexec
- rw - rw - r--.  1 a a  484 Apr 11 09 : 54 testexec.c
father wait success, child exit code : 0		//子进程执行成功,父进程等待成功 为： 0。
testexec ... end!
[a@192 i]$


————————————————————————————————————————————————————————————————

			< exec1 >
int exec1(const char* path, Iconst char* arg, ...);		// '...'可变参数

exec1	程序替换：//l ==> list 列表 :在命令行中怎么执行，就怎么传参
例如：【$ ls -a -l】 
 execl("你想执行谁？(路径)", "你想怎么执行它", NULL)	
 execl("/user/bin/ls", "ls", "-a", "l" , NULL)				//命令选项传完了，必须以 NULL 结尾。
path: 我们要执行的程序，需要带路径(怎么找到要运行的程序)


————————————————————————————————————————————————————————————————

			< execv > 
 v: vector
 execv("你想执行谁？(路径)", "你想怎么执行它")	
int execv(const char* path, char* const argv[]);

			< execvp >
 p: 查找这个程序，系统会自动在环境变量 PATH 中进行查找（无需带路径，直接传文件名，直接告诉 exec* 我要执行谁）
 execl("你想执行谁？", "你想怎么执行它", NULL)	
int execvp(const char* file, char* const argv[]); 


int main()
{
    printf("testexec ... begin!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //child
        sleep(2);
        char* const argv[] =
        {
                (char*)"ls",
                (char*)"-l",
                (char*)"-a",
                (char*)"--color",
                NULL
        };
        //execl("/usr/bin/ls", "ls", "-l", "-a", NULL);	// （执行谁？(路径)，怎么执行，NULL）
        execlp("ls", "-a", NULL);							// （执行谁？(自动查找)，怎么执行，NULL）
        //execv("/usr/bin/ls", argv);
        //execvp("ls", argv);
        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");

    return 0;
}


执行了 ls 命令
[a@192 i]$ . / testexec
testexec ... begin!
total 20
drwxrwxr - x.  2 a a   56 Apr 12 00:12 .
drwxrwxr - x. 14 a a  198 Apr  6 22 : 28 ..
- rw - rw - r--.  1 a a  109 Apr  6 22 : 31 Makefile
- rwxrwxr - x.  1 a a 8704 Apr 12 00 : 12 testexec
- rw - rw - r--.  1 a a  628 Apr 12 00 : 12 testexec.c
father wait success, child exit code : 0
testexec ... end!
[a@192 i]$

————————————————————————————————————————————————————————————————

			< execvpe >
 e: environment 环境变量

int execvpe(const char* file，char* const argv[],char* const envp[]);


————————————————————————————————————————————————————————————————

		《 Makefile ：多个可执行 》

默认是形参第一个可执行。
一次形参多个可执行文件。

.PHONY:all
all : testexec mypragma
testexec : testexec.c
gcc - o $@ $ ^
mypragma:mypragma.cc
g++ - o $@ $ ^ -std = c++11
.PHONY:clean
clean :
rm - f testexec mypragma

————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————

			《 程序替换执行 》
程序替换，并没有形成新的进程。

			< testexec.c >

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("testexec ... begin!\n");

    pid_t id = fork();
    if (id == 0)
    {
        //child
        printf("child pid: %d\n", getpid());
        sleep(2);
        execl("./mypragma", "mypragma", NULL);  //程序替换，直接吧后面的代码替换为， ./mypragma(当前路径下的mypragma)。
        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");

    return 0;
}


//			< mypragma.cc >

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    cout << "hello C++, I am a C++ pragma!" << getpid() << endl;
    cout << "hello C++, I am a C++ pragma!" << getpid() << endl;
    cout << "hello C++, I am a C++ pragma!" << getpid() << endl;

    return 0;
}


结果：
[a@192 i]$ . / testexec
testexec ... begin!
child pid : 4585
hello C++, I am a C++ pragma!4585		//执行了替换的程序，但 pid 未发生改变，证明还是同一个进程。
hello C++, I am a C++ pragma!4585
hello C++, I am a C++ pragma!4585
father wait success, child exit code : 0
testexec ... end!
[a@192 i]$


————————————————————————————————————————————————————————————————


			< shell 脚本>

同样可以替换为其他语言，比如 shell 脚本语言：
 
#!/usr/bin/bash

cnt = 0
while[$cnt - le 5]
do
echo "hello shell, cnt: ${cnt}"
let cnt++
done

[a@192 i]$ bash test.sh
hello shell, cnt: 0
hello shell, cnt : 1
hello shell, cnt : 2
hello shell, cnt : 3
hello shell, cnt : 4
hello shell, cnt : 5
[a@192 i]$


int main()
{
    printf("testexec ... begin!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //child
        printf("child pid: %d\n", getpid());
        sleep(2);
        //execl("./mypragma", "mypragma", NULL);
        execl("/usr/bin/bash", "bash", "test.sh", NULL);
        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");
    return 0;
}

		
 
[a@192 i]$ . / testexec
testexec ... begin!
child pid : 4902
hello shell, cnt : 0
hello shell, cnt : 1
hello shell, cnt : 2
hello shell, cnt : 3
hello shell, cnt : 4
hello shell, cnt : 5
father wait success, child exit code : 0
testexec ... end!
[a@192 i]$


————————————————————————————————————————————————————————————————

也可以直接添加权限来直接跑。
chmod +x test.py		//phtion	语言
 ./test.py

chmod +x test.sh		//shell 脚本语言
 ./test.sh
	
 结果：
hello shell, cnt : 0
hello shell, cnt : 1
hello shell, cnt : 2
hello shell, cnt : 3
hello shell, cnt : 4
hello shell, cnt : 5

————————————————————————————————————————————————————————————————


bash 有两张表：命令行参数表， 环境变量表

          < mypragma.cc >

#include <iostream>
#include <unistd.h>

using namespace std;

 //				 命令行参数， 环境变量(整体替换所有的环境变量)
int main(int argc, char* hh[], char* hehe[])
{
    int i = 0;
    for (i = 0; hh[i]; i++)
    {
        printf("hh[%d]: %s\n", i, hh[i]);
    }
    printf("-------------------------------------------\n");
    for (i = 0; hehe[i]; i++)
    {
        printf("hehe[%d]: %s\n", i, hehe[i]);
    }
    printf("-------------------------------------------\n");

    cout << "hello C++, I am a C++ pragma!" << getpid() << endl;
    cout << "hello C++, I am a C++ pragma!" << getpid() << endl;
    cout << "hello C++, I am a C++ pragma!" << getpid() << endl;

    return 0;
}


			< testexec.c >

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("testexec ... begin!\n");

    pid_t id = fork();
    if (id == 0)
    {
			putenv("HHHHH=1111111111111111");		//新增环境变量。添加到环境变量 environ 表中，而不是整体替换了。	
        //child	//我的父进程本身就有一批环境变量， 这批环境变量从 bash 来
        char* const hh[] =
        {
                (char*)"mypragma",
                (char*)"-a",			//也可以选择传选项！
                (char*)"-l",
                NULL
        };
        char* const hehe[] =
        {
                (char*)"HAHA=11111111",
                (char*)"HEHE=22222222",
                NULL
        };
		 
		//extern char**environ;			//环境变量的第三方指针。
       
		printf("child pid: %d\n", getpid());
       sleep(2);
       
		// execvpe("./mypragma", hh, environ); //把系统默认的环境变量（我的父进程的环境变量(从 bash 来)），交给子进程。 结果：整体替换所有的环境变量。
        execvpe("./mypragma", hh, hehe);

        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");

    return 0;
}


[a@192 i]$ . / testexec
testexec ... begin!
child pid : 5645
hh[0] : mypragma
hh[1] : -a
hh[2] : -l
------------------------------------------ -
hehe[0] : HAHA = 11111111
hehe[1] : HEHE = 22222222
------------------------------------------ -
hello C++, I am a C++ pragma!5645
hello C++, I am a C++ pragma!5645
hello C++, I am a C++ pragma!5645
father wait success, child exit code : 0
testexec ... end!
[a@192 i]$


————————————————————————————————————————————————————————————————

 替换所有的环境变量结果：
[a@192 i]$ . / testexec
testexec ... begin!
child pid : 5822
hh[0] : mypragma
hh[1] : -a
hh[2] : -l
------------------------------------------ -
hehe[0] : XDG_VTNR = 1
hehe[1] : XDG_SESSION_ID = 2
hehe[2] : HOSTNAME = 192.168.35.128
hehe[3] : SHELL = / bin / bash
hehe[4] : TERM = xterm - 256color
hehe[5] : HISTSIZE = 1000
hehe[6] : USER = a
hehe[7] : LS_COLORS = rs = 0 : di = 38; 5; 27:
ln = 38; 5; 51:mh = 44; 38; 5; 15:pi = 40; 38;
5; 11:so = 38; 5; 13:do = 38; 5; 5:bd = 48; 5;
232; 38; 5; 1
...
...
------------------------------------------ -
hello C++, I am a C++ pragma!5822
hello C++, I am a C++ pragma!5822
hello C++, I am a C++ pragma!5822
father wait success, child exit code : 0
testexec ... end!
[a@192 i]$ ^ C


————————————————————————————————————————————————————————————————

环境变量一般有3种情况：
1.用全新的(自己构建)
2.用老的环境变量给子进程，environ
3.添加到老的环境变量中，给子进程 putenv

————————————————————————————————————————————————————————————————

 EXECVE(2号手册)，系统调用
int execve(const char* filename，char* const argv[],char* const envp[]);

【 execlp 】      【 execl 】      【 execle 】
  ↓                  ↓               ↓
 把可变参数保存到  以NULL结尾的  指针数组中.   
  ↓                  ↓               ↓
  ↓                  ↓               ↓
  ↓                  ↓               ↓
【 execbp 】→→→→【 execv 】→→→→【 execve 】(其余几项都是 execve 的封装)
           依次在PATH          使用enviorn
         环境变量指示的        所指向的当前
       各目录中查找该程序      环境变量表


————————————————————————————————————————————————————————————————
